
class Cloud {
  Sprite sprite_;
  vec4 position_;
  vec4 halfExtents_;
  float xVelocity_;

  bool waiting_;

public:

  Cloud() {}

  void startup(vec4 position, vec4 halfExtents, float velocity, int texture) {
    position_ = position;
    halfExtents_ = halfExtents;
    xVelocity_ = velocity;
    sprite_ = Sprite(texture, halfExtents);
    waiting_ = true;
  }

  void reset(vec4 position) {
    waiting_ = true;
  }

  void update() {
    position_[0] = position_[0] + xVelocity_;
    position_[1] = position_[1] - 0.2f;
  }

  void draw(shader &shader, texture* tex, mat4 &modelToProjection) {
    sprite_.draw(position_, true, shader, tex, modelToProjection);
  }

  bool isWaiting() {return waiting_;}

  void go(float velocity) {
    waiting_ = false;
    xVelocity_ = velocity;
  }

  float getX() {return position_[0];}
  float getHalfWidth() {return halfExtents_[0];}

  void setPosition(vec4 p) {position_ = p;}
  void wait() {waiting_ = true;}

};

class CloudManager {
  static const int MAX_CLOUDS = 12;
  
  Cloud clouds_[MAX_CLOUDS];
  vec4 bounds_; // order: left, top, right, bottom
  bool producingClouds_;

  CloudManager() {}

public:

  static CloudManager &get() {
    static CloudManager singleton;
    return singleton;
  }

  void startup(vec4 bounds) {
    
    bounds_ = bounds;

    vec4 halfExtents(128, 128, 0, 0);

    clouds_[0].startup(vec4(bounds_[2] + halfExtents[0], 0, 0, 1), halfExtents, 0, TX_CLOUD_1);
    clouds_[1].startup(vec4(bounds_[2] + halfExtents[0], 0, 0, 1), halfExtents, 0, TX_CLOUD_2);
    clouds_[2].startup(vec4(bounds_[2] + halfExtents[0], 0, 0, 1), halfExtents, 0, TX_CLOUD_3);
    clouds_[3].startup(vec4(bounds_[2] + halfExtents[0], 0, 0, 1), halfExtents, 0, TX_CLOUD_4);

    clouds_[4].startup(vec4(bounds_[2] + halfExtents[0], 0, 0, 1), halfExtents, 0, TX_CLOUD_1);
    clouds_[5].startup(vec4(bounds_[2] + halfExtents[0], 0, 0, 1), halfExtents, 0, TX_CLOUD_2);
    clouds_[6].startup(vec4(bounds_[2] + halfExtents[0], 0, 0, 1), halfExtents, 0, TX_CLOUD_3);
    clouds_[7].startup(vec4(bounds_[2] + halfExtents[0], 0, 0, 1), halfExtents, 0, TX_CLOUD_4);
    
    clouds_[8].startup(vec4(bounds_[2] + halfExtents[0], 0, 0, 1), halfExtents, 0, TX_CLOUD_1);
    clouds_[9].startup(vec4(bounds_[2] + halfExtents[0], 0, 0, 1), halfExtents, 0, TX_CLOUD_2);
    clouds_[10].startup(vec4(bounds_[2] + halfExtents[0], 0, 0, 1), halfExtents, 0, TX_CLOUD_3);
    clouds_[11].startup(vec4(bounds_[2] + halfExtents[0], 0, 0, 1), halfExtents, 0, TX_CLOUD_4);


    producingClouds_ = true;
  }

  void reset() {
    producingClouds_ = true;
    for (int i = 0; i != MAX_CLOUDS; ++i) {
      clouds_[i].wait();
      clouds_[i].setPosition(vec4(bounds_[2] + clouds_[i].getHalfWidth(), 0, 0, 1));
    }
  }

  void update() {
    if (producingClouds_) {
      unsigned long seconds = (unsigned long)time(NULL);
      float chance = Mother(&seconds) * 100;
      if (chance < 0.9f) {
        for(int i = 0; i != MAX_CLOUDS; ++i) {
          if(clouds_[i].isWaiting()) {
            seconds = (unsigned long)time(NULL);
            float random = Mother(&seconds) * -0.4f - 0.1f;
            clouds_[i].go(random);
            break;
          }
        }
      }
    }
  

    for (int i = 0; i != MAX_CLOUDS; ++i) {
      if (!clouds_[i].isWaiting()) {
        clouds_[i].update();

        if (clouds_[i].getX() + clouds_[i].getHalfWidth() < bounds_[0]) {
          clouds_[i].setPosition(vec4(bounds_[2] + clouds_[i].getHalfWidth(), 0, 0, 1));
          clouds_[i].wait();
        }
      }
    }
  }

  void draw(shader &shader, texture* tex, mat4 &modelToProjection) {
    for (int i = 0; i != MAX_CLOUDS; ++i) {
      if (!clouds_[i].isWaiting()) {
        clouds_[i].draw(shader, tex, modelToProjection);
      }
    }
  }

  void stopProducingClouds() {producingClouds_ = false;}
};
